﻿/*                              
   Copyright 2011 Nils Kopal, Christopher Krüger, Simone Sauer, Dennis Nolte, Uni Duisburg-Essen

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using Sharebert.SearchLibrary;
using System.Net;
using System.Collections;
using SharebertServer.Properties;

namespace SharebertServer
{
   
    class IpFileOffers
    {
        public string Ip { get; set; }
        public int Port { get; set; }
        public FileOffer[] FileOffers { get; set; }
        public DateTime TimeStamp { get; set; }
    }

    public class SharebertServer
    {
        private static ArrayList _ipFileOffers = ArrayList.Synchronized(new ArrayList());
        private static object _lock = new object();

        public static void Main(String[] args)
        {
            try
            {
                Console.WriteLine("Welcome to Sharebert Index Server\r\nTo stop this application please just close this window");
                Console.WriteLine("- Configured listen port is: " + Settings.Default.ListenPort);
                Console.WriteLine("- Configured check interval is: " + Settings.Default.CheckInterval);
                Console.WriteLine("- Configured delete interval is: " + Settings.Default.DeleteInterval);
                Console.WriteLine("Have fun...");
                var server = new SharebertServer();
                server.Start();
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }
        }

        private void Start()
        {
            var timer = new Timer();
            timer.Interval = Settings.Default.CheckInterval;
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.Start();

            var server = new IndexServer(IPAddress.Any, Settings.Default.ListenPort);
            server.Timeout = 30;
            server.OfferReceived += new EventHandler<OfferedFilesEventArgs>(HandleOfferedFiles);
            server.SearchReceived += new EventHandler<SearchReceivedEventArgs>(HandleSearchRequest);
            server.Start();
        }

        private void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            lock (_lock)
            {
                Console.WriteLine("Checking file ages now");
                var result = from IpFileOffers ipFileOffer in (ArrayList)_ipFileOffers.Clone()
                             where
                                 (ipFileOffer.TimeStamp.AddMilliseconds(Settings.Default.DeleteInterval) <= DateTime.Now)
                             select ipFileOffer;
                foreach (var ipFileOffer in result)
                {
                    Console.WriteLine("Removing FileOffers from " + ipFileOffer.Ip + " because these are too old");
                    _ipFileOffers.Remove(ipFileOffer);
                }
            }
        }

        private static void HandleOfferedFiles(object sender, OfferedFilesEventArgs e)
        {
            lock (_lock)
            {
                try
                {
                    Console.WriteLine("Got FileOffers from " + e.Client.RemoteIP + ":" + e.Client.RemotePort);
                    var result = from IpFileOffers ipFileOffer in (ArrayList)_ipFileOffers.Clone()
                                 where
                                     (ipFileOffer.Ip.Equals(e.Client.RemoteIP.ToString()))
                                 select ipFileOffer;

                    foreach (var ipFileOffer in result)
                    {
                        _ipFileOffers.Remove(ipFileOffer);
                    }

                    Console.WriteLine("Add new FileOffer from " + e.Client.RemoteIP + ":" + e.Client.RemotePort);

                    foreach (var fileOffer in e.FileOffer.Files)
                    {
                        Console.WriteLine(" Added file '" + fileOffer.Name + "' with " + fileOffer.Size + " bytes.");
                    }

                    _ipFileOffers.Add(new IpFileOffers()
                    {
                        FileOffers = e.FileOffer.Files,
                        Ip = e.Client.RemoteIP.ToString(),
                        Port = e.FileOffer.clientPort,
                        TimeStamp = DateTime.Now
                    });

                    e.Client.Disconnect();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.WriteLine(ex.StackTrace);
                }
            }
        }

        private static void HandleSearchRequest(object sender, SearchReceivedEventArgs e)
        {
            lock (_lock)
            {
                try
                {
                    System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(e.RegEx, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                    Console.WriteLine("Got Search for '" + e.RegEx + "' from " + e.Client.RemoteIP + ":" +
                                      e.Client.RemotePort);
                    var result = new List<SearchResult>();
                    foreach (IpFileOffers ipFileOffer in _ipFileOffers)
                    {
                        var fileoffers = ipFileOffer.FileOffers.Where(
                            fileOffer => regex.IsMatch(fileOffer.Name)).ToList();
                        if (fileoffers.Count > 0)
                        {
                            //we only send file offers from "other" clients
                            if ((!ipFileOffer.Ip.ToString().Equals(e.Client.RemoteIP.ToString())) || (!ipFileOffer.Port.ToString().Equals(e.Client.RemotePort.ToString())))
                            {
                                result.Add(new SearchResult()
                                               {
                                                   ClientIP = ipFileOffer.Ip.ToString(),
                                                   ClientPort = ipFileOffer.Port,
                                                   OfferingFiles = fileoffers.ToArray()
                                               });
                            }
                        }
                        foreach (var searchResult in fileoffers)
                        {
                            Console.WriteLine(e.Client.RemoteIP + ":" +
                                      e.Client.RemotePort + " - Regex matches to '" + searchResult.Name + "' with "
                                      + Math.Round((double)searchResult.Size / 1024 / 1024, 2, MidpointRounding.ToEven)
                                      + " Megabyte located on " + ipFileOffer.Ip + ":" + ipFileOffer.Port);
                        }
                    }

                    e.Client.SendResult(result.ToArray());
                    e.Client.Disconnect();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.WriteLine(ex.StackTrace);
                }
            }
        }       
    }
}
